package com.hd123.rumba.gwt.widget2.client.dialog;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.widget2.client.misc.RMask;
import com.hd123.rumba.gwt.widget2.client.misc.RWidgetMask;

/**
 * “正在加载”对话框
 * 
 * @author LiQi
 * 
 */
public class RLoadingDialog {

  public static final String STYLENAME = "rb-LoadingDialog";
  public static final String STYLENAME_OUTER = "rb-LoadingDialog-outer";
  public static final String STYLENAME_INNER = "rb-LoadingDialog-inner";

  public static final String DEFAULT_LOADING_TEXT = "正在加载...";

  /** 多层对话框堆栈 */
  private static Map<Widget, Stack<RLoadingDialog>> dialogStacks = new HashMap();

  /** 对应的控件 */
  private Widget widget;

  /** 文本标签 */
  private HTML label;

  /** 对话框 */
  private PopupPanel dialog;

  /** 是否显示遮罩 */
  private boolean showMask = true;

  /**
   * @param widget
   *          对应的控件，允许null，表示整个窗口上显示
   * @param html
   *          显示的文字，可以包含HTML标记，允许null，此时使用
   *          {@link #DEFAULT_LOADING_TEXT}
   * @param showMask
   *          是否显示遮罩
   */
  protected RLoadingDialog(Widget widget, String html, boolean showMask) {
    this.widget = widget;
    this.showMask = showMask;

    dialog = new PopupPanel(false, true);
    dialog.setStyleName(STYLENAME);
    HorizontalPanel outerPanel = new HorizontalPanel();
    outerPanel.setStyleName(STYLENAME_OUTER);
    outerPanel.setSpacing(2);
    dialog.setWidget(outerPanel);

    HorizontalPanel innerPanel = new HorizontalPanel();
    innerPanel.setStyleName(STYLENAME_INNER);
    outerPanel.add(innerPanel);

    label = new HTML(html == null ? DEFAULT_LOADING_TEXT : html);
    innerPanel.add(label);

    Window.addResizeHandler(new WindowHandler());
  }

  /**
   * 显示对话框，带遮罩。显示的文字为"正在加载..."。
   * 
   */
  public static void show() {
    show(null, null, true);
  }

  /**
   * 显示对话框，带遮罩
   * 
   * @param html
   *          显示的文字，可以包含HTML标记，允许null，此时使用
   *          {@link #DEFAULT_LOADING_TEXT}
   */
  public static void show(String html) {
    show(null, html, true);
  }

  /**
   * 显示对话框，不带遮罩。显示的文字为"正在加载..."。
   * 
   */
  public static void showWithoutMask() {
    show(null, null, false);
  }

  /**
   * 显示对话框，不带遮罩
   * 
   * @param html
   *          显示的文字，可以包含HTML标记，允许null，此时使用
   *          {@link #DEFAULT_LOADING_TEXT}
   */
  public static void showWithoutMask(String html) {
    show(null, html, false);
  }

  /**
   * 显示对话框，带遮罩。显示的文字为"正在加载..."。
   * 
   * @param widget
   *          对应的控件，允许null，表示整个窗口上显示
   */
  public static void show(Widget widget) {
    show(widget, null, true);
  }

  /**
   * 显示对话框，带遮罩。
   * 
   * @param widget
   *          对应的控件，允许null，表示整个窗口上显示
   * @param html
   *          显示的文字，可以包含HTML标记，允许null，此时使用
   *          {@link #DEFAULT_LOADING_TEXT}
   */
  public static void show(Widget widget, String html) {
    show(widget, html, true);
  }

  /**
   * 显示对话框
   * 
   * @param widget
   *          对应的控件，允许null，表示整个窗口上显示
   * @param html
   *          显示的文字，可以包含HTML标记，允许null，此时使用
   *          {@link #DEFAULT_LOADING_TEXT}
   * @param showMask
   *          是否显示遮罩
   */
  public static void show(Widget widget, String html, boolean showMask) {
    // widget对应的堆栈
    Stack stack = dialogStacks.get(widget);
    if (stack == null) {
      stack = new Stack();
      dialogStacks.put(widget, stack);
    }

    RLoadingDialog loadingDialog = new RLoadingDialog(widget, html, showMask);
    stack.push(loadingDialog);
    loadingDialog.doShow();
  }

  /**
   * 关闭对话框
   * 
   */
  public static void hide() {
    hide(null);
  }

  /**
   * 关闭对话框
   * 
   * @param widget
   *          对应的控件，允许null，表示关闭整个窗口上的对话框
   */
  public static void hide(Widget widget) {
    // widget对应的堆栈
    Stack<RLoadingDialog> stack = dialogStacks.get(widget);
    if (stack == null || stack.isEmpty())
      return;

    RLoadingDialog dialog = stack.pop();
    dialog.doHide();
  }

  /**
   * 关闭对话框，如果存在多层对话框，则全部关闭
   * 
   */
  public static void hideAll() {
    hideAll(null);
  }

  /**
   * 关闭对话框，如果存在多层对话框，则全部关闭
   * 
   * @param widget
   *          对应的控件，允许null，表示关闭整个窗口上的对话框
   */
  public static void hideAll(Widget widget) {
    Stack<RLoadingDialog> stack = dialogStacks.get(widget);
    if (stack == null)
      return;

    while (!stack.isEmpty()) {
      RLoadingDialog loadingDialog = stack.pop();
      loadingDialog.doHide();
    }
  }

  /**
   * 控件上是否正在显示对话框
   * 
   * @param widget
   *          对应的控件，允许null，表示整个窗口上显示
   * @return
   */
  public static boolean isShowing(Widget widget) {
    Stack<RLoadingDialog> stack = dialogStacks.get(widget);
    return stack != null && !stack.isEmpty();
  }

  /**
   * 显示对话框
   * 
   */
  protected void doShow() {
    // 遮罩
    if (showMask) {
      if (widget == null)
        RMask.show();
      else
        RWidgetMask.show(widget);
    }

    // 在控件上显示时对话框不能模态
    dialog.setModal(widget == null);

    dialog.show();
    center();
  }

  /**
   * 使对话框居中显示
   * 
   */
  protected void center() {
    if (widget == null) {
      dialog.center();
    } else {
      // 定位在控件的正中
      int left = widget.getAbsoluteLeft() + (widget.getOffsetWidth() - dialog.getOffsetWidth()) / 2;
      int top = widget.getAbsoluteTop() + (widget.getOffsetHeight() - dialog.getOffsetHeight()) / 2;
      dialog.setPopupPosition(left, top);
    }
  }

  /**
   * 隐藏对话框
   * 
   */
  protected void doHide() {
    if (dialog == null)
      return;

    dialog.hide();

    // 遮罩
    if (showMask) {
      if (widget == null)
        RMask.hide();
      else
        RWidgetMask.hide(widget);
    }
  }

  /**
   * 窗口事件处理，保证在窗口改变大小时对话框保持居中
   * 
   * @author LiQi
   * 
   */
  private class WindowHandler implements ResizeHandler {
    public void onResize(ResizeEvent event) {
      if (dialog.isShowing()) {
        DeferredCommand.addCommand(new Command() {
          public void execute() {
            center();
          }
        });
      }
    }
  }
}
